package com.varian.redis.util;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.extra.spring.SpringUtil;
import com.varian.tool.exception.ServerException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.options.CommonOptions;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.Duration;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @author ben
 * @since 2024/6/23
 */
@Slf4j
@UtilityClass
public class RedisUtil {

    RedisTemplate<String, Object> template;
    Redisson redisson;

    public void init() {
        if (template == null) {
            template = SpringUtil.getBean(new TypeReference<>() {
            });
        }
        if (redisson == null) {
            redisson = SpringUtil.getBean(Redisson.class);
        }
    }

    @SuppressWarnings("unchecked")
    public <V> RedisTemplate<String, V> getTemplate() {
        return (RedisTemplate<String, V>) template;
    }

    public RLock getLock(String key, long timeout) {
        CommonOptions options = CommonOptions.name(key);
        options.timeout(Duration.ofSeconds(timeout));
        return redisson.getLock(options);
    }

    public RLock getLock(String key) {
        return redisson.getLock(key);
    }

    public <T> T lock(String key, Runnable runnable) {
        return lock(key, () -> {
            runnable.run();
            return null;
        });
    }

    public <T> T lock(String key, Supplier<T> supplier) {
        RLock lock = getLock(key);
        try {
            if (lock.tryLock()) {
                return supplier.get();
            }
            return null;
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
    }

    public void send(String channel, Object message) {
        try {
            getTemplate().convertAndSend(channel, message);
        } catch (Exception e) {
            log.error("发送消息失败", e);
        }
    }

    public void del(String key) {
        getTemplate().delete(key);
    }

    public void expire(String key, long timeout, TimeUnit unit) {
        getTemplate().expire(key, timeout, unit);
    }

    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(getTemplate().hasKey(key));
    }

    @UtilityClass
    public static class Value {

        public <T> T get(String key) {
            return RedisUtil.<T>getTemplate().opsForValue().get(key);
        }

        public <T> T getAndDel(String key) {
            return RedisUtil.<T>getTemplate().opsForValue().getAndDelete(key);
        }

        public void set(String key, Object value) {
            getTemplate().opsForValue().set(key, value);
        }

        public void set(String key, Object value, long timeout, TimeUnit unit) {
            getTemplate().opsForValue().set(key, value, timeout, unit);
        }
    }

    @UtilityClass
    public static class Hash {
        public <HK, HV> Map<HK, HV> entries(String key) {
            Assert.notBlank(key, () -> ServerException.of("key不能为空"));
            return getTemplate().<HK, HV>opsForHash().entries(key);
        }

        public void putAll(String key, Map<String, Object> map) {
            getTemplate().opsForHash().putAll(key, map);
        }

        public void put(String key, String hashKey, Object hashValue) {
            getTemplate().opsForHash().put(key, hashKey, hashValue);
        }

        public boolean del(String key, Object... hashKeys) {
            return getTemplate().opsForHash().delete(key, hashKeys) > 0;
        }
    }

}

